package cn.sciento.transfer.infra.liquibase.parser.data;

import liquibase.database.Database;
import liquibase.diff.DiffResult;
import liquibase.diff.ObjectDifferences;
import liquibase.diff.compare.CompareControl;
import liquibase.diff.compare.DatabaseObjectComparatorFactory;
import liquibase.diff.core.StandardDiffGenerator;
import liquibase.exception.DatabaseException;
import liquibase.snapshot.DatabaseSnapshot;
import liquibase.structure.DatabaseObject;
import liquibase.structure.core.Schema;
import liquibase.structure.core.Table;
import liquibase.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;

public class STongStandardDiffGenerator extends StandardDiffGenerator {
  private static final Logger LOGGER = LoggerFactory.getLogger(STongStandardDiffGenerator.class);
  
  private static final ThreadLocal<String> REFERENCE_TABLE_NAME_HOLDER = new ThreadLocal<>();
  
  private static final ThreadLocal<String> COMPARISON_TABLE_NAME_HOLDER = new ThreadLocal<>();
  
  public int getPriority() {
    return 5;
  }
  
  public DiffResult compare(DatabaseSnapshot referenceSnapshot, DatabaseSnapshot comparisonSnapshot, CompareControl compareControl) throws DatabaseException {
    DiffResult diffResult = super.compare(referenceSnapshot, comparisonSnapshot, compareControl);
    String referenceTableName = REFERENCE_TABLE_NAME_HOLDER.get();
    String comparisonTableName = COMPARISON_TABLE_NAME_HOLDER.get();
    Table referenceTable = internalFindTable(referenceSnapshot, referenceTableName);
    Assert.notNull(referenceTable, "reference table cannot be null.");
    referenceTable.setName(comparisonTableName);
    return diffResult;
  }
  
  protected <T extends DatabaseObject> void compareObjectType(Class<T> type, DatabaseSnapshot referenceSnapshot, DatabaseSnapshot comparisonSnapshot, DiffResult diffResult) {
    String referenceTableName = REFERENCE_TABLE_NAME_HOLDER.get();
    String comparisonTableName = COMPARISON_TABLE_NAME_HOLDER.get();
    Table referenceTable = internalFindTable(referenceSnapshot, referenceTableName);
    Table comparisonTable = internalFindTable(comparisonSnapshot, comparisonTableName);
    if (referenceTable == null) {
      LOGGER.info("reference table {} doesn't exists in reference database. exit diff.", referenceTableName);
      return;
    } 
    if (comparisonTable == null) {
      LOGGER.info("comparison table is null.");
      return;
    } 
    Database comparisonDatabase = comparisonSnapshot.getDatabase();
    Database referenceDatabase = referenceSnapshot.getDatabase();
    CompareControl.SchemaComparison[] schemaComparisons = diffResult.getCompareControl().getSchemaComparisons();
    if (schemaComparisons != null)
      for (CompareControl.SchemaComparison schemaComparison : schemaComparisons) {
        for (DatabaseObject databaseObject1 : referenceSnapshot.get(type)) {
          Schema referenceObjectSchema = databaseObject1.getSchema();
          if (referenceObjectSchema != null && referenceObjectSchema.getName() != null)
            if (!StringUtils.trimToEmpty(referenceObjectSchema.toCatalogAndSchema().standardize(referenceDatabase).getSchemaName()).equalsIgnoreCase(
                StringUtils.trimToEmpty(schemaComparison.getReferenceSchema()
                  .standardize(referenceDatabase).getSchemaName())))
              continue;  
          referenceTable.setName(comparisonTableName);
          DatabaseObject databaseObject2 = comparisonSnapshot.get(databaseObject1);
          if (databaseObject2 == null) {
            diffResult.addMissingObject(databaseObject1);
          } else {
            ObjectDifferences differences = DatabaseObjectComparatorFactory.getInstance().findDifferences(databaseObject1, databaseObject2, comparisonDatabase, diffResult.getCompareControl());
            if (differences.hasDifferences())
              diffResult.addChangedObject(databaseObject1, differences); 
          } 
          referenceTable.setName(referenceTableName);
        } 
      }  
  }
  
  private Table internalFindTable(DatabaseSnapshot databaseSnapshot, String targetTableName) {
    for (Table table : databaseSnapshot.get(Table.class)) {
      if (targetTableName.equals(table.getName()))
        return table; 
    } 
    return null;
  }
  
  public static void setDiffTableName(String referenceTableName, String comparisonTableName) {
    REFERENCE_TABLE_NAME_HOLDER.set(referenceTableName);
    COMPARISON_TABLE_NAME_HOLDER.set(comparisonTableName);
  }
  
  public static void clearDiffTableName() {
    REFERENCE_TABLE_NAME_HOLDER.remove();
    COMPARISON_TABLE_NAME_HOLDER.remove();
  }
}
